﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Gezel.Simulator.Reference.IpBlocks
{
	[IpBlockAttribute("queue")]
	public class QueueBlock : AbstractIpBlock
	{
		private const int DataInPinNumber		= 0;
		private const int CanWritePinNumber = 3;
		private const int DoWritePinNumber	= 1;

		private const int DataOutPinNumber	= 4;
		private const int CanReadPinNumber	= 5;
		private const int DoReadPinNumber		= 2;		

		private int DataInPinValue
		{
			get { return ioValues[DataInPinNumber].ToInt(); }			
		}

		private int CanWritePinValue
		{
			get { return ioValues[CanWritePinNumber].ToInt(); }
			set { ioValues[CanWritePinNumber].Assign(value); }
		}

		private int DoWritePinValue
		{
			get { return ioValues[DoWritePinNumber].ToInt(); }
		}

		private int DataOutPinValue
		{
			get { return ioValues[DataOutPinNumber].ToInt(); }
			set { ioValues[DataOutPinNumber].Assign(value); }
		}

		private int CanReadPinValue
		{
			get { return ioValues[CanReadPinNumber].ToInt(); }
			set { ioValues[CanReadPinNumber].Assign(value); }
		}

		private int DoReadPinValue
		{
			get { return ioValues[DoReadPinNumber].ToInt(); }
		}

		private string name;

		private int queueLength = 1;
		private Queue<int> queue = new Queue<int>();

		private bool QueueIsFull
		{
			get { return queue.Count == queueLength; }
		}

		private bool QueueIsEmpty
		{
			get { return queue.Count == 0; }
		}

		public QueueBlock(string name) : base(name)
		{
		}

		public override void SetParameter(string _name)
		{
			string field = _name.Substring(0, _name.IndexOf('='));
      string value = _name.Substring(_name.IndexOf('=') + 1);

			if (field == "length")
				queueLength = int.Parse(value);

			if (field == "name")
				this.name = (string) value.Clone();
		}

		public override void Run()
		{
			try
			{
				// First, if the producer wants to write and the queue has room
				// insert the incomming data into the queue.
				if (!QueueIsFull && DoWritePinValue == 1)
				{					
					queue.Enqueue(DataInPinValue);
				}
				
				// Secondly, if the consumer 
				if (!QueueIsEmpty && DoReadPinValue == 1)
				{
					int v = queue.Dequeue();				
					DataOutPinValue = v;
				}
				else
				{
					DataOutPinValue = queue.Count == 0 ? 0 : queue.Peek();
				}

				CanReadPinValue =  !QueueIsEmpty || DoWritePinValue == 1 ? 1 : 0;
				CanWritePinValue = !QueueIsFull ? 1 : 0;
			}
			catch (Exception e)
			{
				String message = e.GetType().ToString() + " " +
					e.Message + " trace: \n" + e.StackTrace;

				System.Windows.Forms.MessageBox.Show(message);
			}
		}

		public override bool CheckTerminal(int n, string tname, IoDirection dir)
		{
			Console.WriteLine("(queue) n: {0} \t dir: {2} \t tname: {1}", n, tname, dir);

			switch (n)
			{
				case 0:
					return (dir == AbstractIpBlock.IoDirection.Input && tname == "data_in");
				case 1:
					return (dir == AbstractIpBlock.IoDirection.Output && tname == "can_write");
				case 2:
					return (dir == AbstractIpBlock.IoDirection.Input && tname == "do_write");
				case 3:
					return (dir == AbstractIpBlock.IoDirection.Output && tname == "data_out");
				case 4:
					return (dir == AbstractIpBlock.IoDirection.Output && tname == "can_read");
				case 5:
					return (dir == AbstractIpBlock.IoDirection.Input && tname == "do_read");

				default:
					throw new NotSupportedException("CheckTerminal() unknown 'n' value");
			}
		}
	}
}
